package net.jgrm.common.exception;

import net.jgrm.common.model.Result;
import net.jgrm.common.util.ResultUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.util.Set;

/**
 * 全局异常处理器
 * 统一处理系统中的各种异常，返回标准的响应格式
 *
 * @author service-common
 * @date 2024-09-12
 */
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {

    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 拦截缺少请求参数异常
     *
     * @param e       异常对象
     * @param request 请求对象
     * @return 统一响应结果
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({MissingServletRequestParameterException.class})
    public Result handleMissingServletRequestParameterException(MissingServletRequestParameterException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        logger.error("请求地址'{}', 缺少请求参数: {}", requestURI, e.getMessage());
        SystemExceptionEnum errorEnum = SystemExceptionEnum.MISS_REQUIRED_PARAMETER;
        String message = String.format(errorEnum.getMessage(), e.getParameterName());
        return ResultUtils.getResult(errorEnum.getCode(), message);
    }

    /**
     * 拦截参数解析失败异常
     *
     * @param e       异常对象
     * @param request 请求对象
     * @return 统一响应结果
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({HttpMessageNotReadableException.class})
    public Result handleHttpMessageNotReadableException(HttpMessageNotReadableException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        logger.error("请求地址'{}', 参数解析失败: {}", requestURI, e.getMessage());
        SystemExceptionEnum errorEnum = SystemExceptionEnum.PARAM_ERROR;
        return ResultUtils.getResult(errorEnum.getCode(), errorEnum.getMessage());
    }

    /**
     * 拦截参数绑定失败异常
     *
     * @param e       异常对象
     * @param request 请求对象
     * @return 统一响应结果
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({BindException.class})
    public Result handleBindException(BindException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        logger.error("请求地址'{}', 参数绑定失败: {}", requestURI, e.getMessage());
        SystemExceptionEnum errorEnum = SystemExceptionEnum.PARAMETER_VALUE_INVALID;
        BindingResult result = e.getBindingResult();
        FieldError error = result.getFieldError();
        if (error != null) {
            String field = error.getField();
            String code = error.getDefaultMessage();
            String message = String.format(errorEnum.getMessage(), field, code);
            return ResultUtils.getResult(errorEnum.getCode(), message);
        }
        return ResultUtils.getResult(errorEnum.getCode(), errorEnum.getMessage());
    }

    /**
     * 拦截Hibernate Validator参数验证异常
     *
     * @param e       异常对象
     * @param request 请求对象
     * @return 统一响应结果
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({MethodArgumentNotValidException.class})
    public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        logger.error("请求地址'{}', 参数验证失败: {}", requestURI, e.getMessage());
        SystemExceptionEnum errorEnum = SystemExceptionEnum.PARAMETER_VALUE_INVALID;
        BindingResult result = e.getBindingResult();
        FieldError error = result.getFieldError();
        if (error != null) {
            String message = error.getDefaultMessage();
            return ResultUtils.getResult(errorEnum.getCode(), message);
        }
        return ResultUtils.getResult(errorEnum.getCode(), errorEnum.getMessage());
    }

    /**
     * 拦截约束验证异常
     *
     * @param e       异常对象
     * @param request 请求对象
     * @return 统一响应结果
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({ConstraintViolationException.class})
    public Result handleConstraintViolationException(ConstraintViolationException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        logger.error("请求地址'{}', 约束验证失败: {}", requestURI, e.getMessage());
        SystemExceptionEnum errorEnum = SystemExceptionEnum.PARAM_ERROR;
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        if (!violations.isEmpty()) {
            ConstraintViolation<?> violation = violations.iterator().next();
            return ResultUtils.getResult(errorEnum.getCode(), violation.getMessage());
        }
        return ResultUtils.getResult(errorEnum.getCode(), errorEnum.getMessage());
    }

    /**
     * 拦截参数验证失败异常
     *
     * @param e       异常对象
     * @param request 请求对象
     * @return 统一响应结果
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({ValidationException.class})
    public Result handleValidationException(ValidationException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        logger.error("请求地址'{}', 参数验证失败: {}", requestURI, e.getMessage());
        SystemExceptionEnum errorEnum = SystemExceptionEnum.PARAM_ERROR;
        return ResultUtils.getResult(errorEnum.getCode(), errorEnum.getMessage());
    }

    /**
     * 拦截不支持当前请求方法异常
     *
     * @param e       异常对象
     * @param request 请求对象
     * @return 统一响应结果
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({HttpRequestMethodNotSupportedException.class})
    public Result handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        logger.error("请求地址'{}', 不支持当前请求方法: {}", requestURI, e.getMessage());
        SystemExceptionEnum errorEnum = SystemExceptionEnum.UNSUPPORT_HTTP_METHOD;
        return ResultUtils.getResult(errorEnum.getCode(), errorEnum.getMessage());
    }

    /**
     * 拦截不支持当前媒体类型异常
     *
     * @param e       异常对象
     * @param request 请求对象
     * @return 统一响应结果
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({HttpMediaTypeNotSupportedException.class})
    public Result handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        logger.error("请求地址'{}', 不支持当前媒体类型: {}", requestURI, e.getMessage());
        SystemExceptionEnum errorEnum = SystemExceptionEnum.UNSUPPORT_MEDIATYPE;
        String message = String.format(errorEnum.getMessage(), e.getContentType());
        return ResultUtils.getResult(errorEnum.getCode(), message);
    }

    /**
     * 拦截通用业务异常
     *
     * @param e       异常对象
     * @param request 请求对象
     * @return 统一响应结果
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({CommonException.class})
    public Result handleCommonException(CommonException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        logger.error("请求地址'{}', 业务异常: {}", requestURI, e.getMessage());
        return ResultUtils.getResult(e.getCode(), e.getMessage());
    }

    /**
     * 拦截所有未知异常
     *
     * @param e       异常对象
     * @param request 请求对象
     * @return 统一响应结果
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({Exception.class})
    public Result handleException(Exception e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        logger.error("请求地址'{}', 服务器未知异常: {}", requestURI, e.getMessage(), e);
        SystemExceptionEnum errorEnum = SystemExceptionEnum.SYSTEM_ERROR;
        return ResultUtils.getResult(errorEnum.getCode(), errorEnum.getMessage());
    }
}